package suites.queue {
	import suites.queue.mock.AsyncQable;
	import suites.queue.mock.ImmediateQable;

	import org.flexunit.asserts.assertEquals;
	import org.flexunit.asserts.assertFalse;
	import org.flexunit.asserts.assertTrue;
	import org.splink.library.queue.IQ;
	import org.splink.library.queue.IQable;
	import org.splink.library.queue.Q;
	import org.splink.library.queue.QEvent;

	/**
	 * @author Max Kugland
	 */
	public class QStopTest extends AbstractQTest {

		[Test]
		public function emptyStop() : void {
			_q.stop();
		}

		[Test]
		public function nestedStop() : void {
			var q1 : IQ = createQ(3);			var q2 : IQ = createQ(2);			var q3 : IQ = createQ(1);			var q4 : IQ = createQ(3);
			_q.add(q1);
			q1.add(q2);
			q2.add(q3);
			q2.add(q4);
			_q.stop();
			
			assertEquals(13, Q.getTotal(_q));
		}

		[Test(async)]
		public function runningStop() : void {
			handleAsync(2000);
			
			_q.add(new ImmediateQable("1"));
			_q.add(new AsyncQable("2"));
			_q.add(new AsyncQable("3"));
			_q.add(new AsyncQable("4"));
			assertEquals(4, Q.getTotal(_q));
			
			var flag : Boolean = true;
			var a : Array = new Array();
			Q.visit(_q, function(n : IQable):void {
				n.register(QEvent.COMPLETE, function(e : QEvent):void {
					a.push(e.source);
					if(e.id == "3" && flag) {
						flag = false;
						_q.stop();
						assertFalse(_q.isRunning);
						_q.start();
						assertTrue(_q.isRunning);
					}
					/*
					 * The enclosing <code>IQ</code> doesn't receive the complete event of
					 * item 3 before it is stopped. That is because this test registeres 
					 * COMPLETE listeners with all enqueued items before the <code>IQ</code> is 
					 * started. The <code>IQ</code> on the other hand registeres a COMPLETE 
					 * listener with each item not until it's <code>start</code> method is 
					 * executed. So the first listener to be called is this function, which 
					 * stops and (re)starts the <code>IQ</code>. This means that the <code>IQ</code> 
					 * never detects the completion of item 3 and restarts it.
					 * The <code>IQ</code> certainly could know about the item completion first and
					 * accordingly act, but that would make listeners on <code>IQable</code>
					 * items quite unusable as they might receive an complete event far too late.
					 * So the lesser evil is that in case a queue is stopped from an item listeners
					 * inside, then the queue executes the again if the queue is started again.
					 */
					if(e.id == "4" && !flag) {
						assertEquals(5, a.length);
					}
				});
			});		
			
			_q.start();	
		}

		[Test(async)]
		public function nestedRunningStop() : void {
			handleAsync(6000);
			var q1 : IQ = createQ(3); //q0
			var q2 : IQ = createQ(2); //q4
			var q3 : IQ = createQ(1); //q7
			var q4 : IQ = createQ(3); //q9
			_q.add(q1);
			q1.add(q2);
			q2.add(q3);
			q2.add(q4);
			
			assertEquals(13, Q.getTotal(_q));
			
			var flag : Boolean = true;
			var a : Array = new Array();
			Q.visit(_q, function(n : IQable):void {
				n.register(QEvent.COMPLETE, function(e : QEvent):void {
					a.push(e.source);
					
					// stop after 12 items and (re)start.
					if(e.id == "q-7" && flag) {
						flag = false;
						_q.stop();
						assertFalse(_q.isRunning);
						_q.start();
						assertTrue(_q.isRunning);
					}
					if(e.id == "q-0" && !flag) {
						assertEquals(14, a.length);
					}
				});
			});		
			
			_q.register(QEvent.COMPLETE, function(e : QEvent):void {
				assertEquals(14, a.length);
			});
			
			_q.start();
		}
	}
}
